NestJS API Versioning With Swagger
Written by Chat GPT Deep Research
NestJS API 버저닝과 Swagger 연동
NestJS는 REST API를 효율적으로 구축할 수 있도록 API 버전 관리(Versioning) 기능을 제공합니다. 또한 @nestjs/swagger
모듈을 사용하면 API 문서를 손쉽게 자동 생성할 수 있습니다. 이 글에서는 URI 기반의 API 버저닝을 NestJS에서 구현하고 Swagger(OpenAPI) 문서화와 통합하는 방법을 살펴보겠습니다. 실무 예제를 통해 단일 Swagger 문서로 모든 버전을 관리하는 방법과, 다중 Swagger 문서로 버전별(또는 모듈별)로 문서를 분리하는 두 가지 전략을 구현하고 비교해보겠습니다.
URI 기반 API 버전 관리 설정 (main.ts)
NestJS에서는 간단한 설정으로 URI에 버전 정보를 포함시킬 수 있습니다. URI 버전 관리는 URL 경로에 /v1
, /v2
처럼 버전 세그먼트를 추가하여 버전을 구분하는 방식입니다. 우선 main.ts
부트스트랩 파일에서 버전 관리 기능을 활성화해야 합니다. Nest 애플리케이션 인스턴스에 대해 app.enableVersioning()
을 호출하며, type: VersioningType.URI
옵션을 지정하면 URI 기반 버저닝이 적용됩니다.
특히 중요한 점은 Swagger 문서를 설정하기 전에 먼저 enableVersioning
을 호출해야 한다는 것입니다. 이는 NestJS Swagger 모듈의 이슈에서도 강조된 부분으로, 순서가 바뀌면 Swagger가 버전이 붙은 경로들을 인식하지 못할 수 있습니다. 다음은 main.ts
의 예시 코드입니다:
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
import { setupSwagger } from './setupSwagger'; // Swagger 설정 함수 import
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// (중요) Swagger 설정 전에 API 버전 관리 활성화
app.enableVersioning({ type: VersioningType.URI });
// Swagger 문서 설정
setupSwagger(app);
await app.listen(3000);
}
bootstrap();
위 코드에서 app.enableVersioning({ type: VersioningType.URI })
한 줄로 전역적인 URI 버전 관리가 활성화됩니다. 이제 컨트롤러나 라우트에 버전 메타데이터를 달면 요청 경로에 해당 버전이 자동으로 추가됩니다 (예: /v1/...
, /v2/...
).
버전별 컨트롤러 구현 (예: ReservationV2Controller)
이제 특정 컨트롤러에 버전을 지정하여 버전에 따라 다른 동작을 하도록 구성할 수 있습니다. NestJS에서는 컨트롤러나 개별 경로 핸들러에 @Version()
데코레이터를 사용하거나, 컨트롤러 데코레이터에 { version: '값' }
옵션을 줘서 해당 버전에서만 동작하도록 만들 수 있습니다. 예를 들어 v2 버전의 API 엔드포인트를 담당하는 ReservationV2Controller
를 구현하면 다음과 같습니다:
// reservation.v2.controller.ts
import { Controller, Get, Post, Param } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
@Controller({ path: 'reservations', version: '2' })
@ApiTags('Reservations')
export class ReservationV2Controller {
@Get()
@ApiOperation({ summary: '모든 예약 목록 조회 (v2)' })
findAllV2() {
// 예약 목록 조회 v2 로직...
}
@Get(':id')
@ApiOperation({ summary: 'ID로 예약 조회 (v2)' })
findByIdV2(@Param('id') id: string) {
// 특정 ID 예약 조회 v2 로직...
}
@Post()
@ApiOperation({ summary: '새 예약 생성 (v2)' })
createV2() {
// 예약 생성 v2 로직...
}
}
위 코드에서는 @Controller({ path: 'reservations', version: '2' })
를 통해 이 컨트롤러의 모든 경로가 v2로 버전이 지정되었습니다. 따라서 예를 들어 findAllV2()
메서드는 GET 요청으로 /v2/reservations
경로를 처리하게 됩니다. (ReservationV1Controller
에서는 version을 '1'로 지정하거나 기본값을 사용해 /v1/reservations
를 처리하도록 구현하면 됩니다.) 각 컨트롤러에 @ApiTags
등을 지정하면 Swagger 문서에서 태그로 구분할 수도 있습니다.
이렇게 각 버전별로 별도 컨트롤러를 구성하면 버전 상승에 따른 변경 사항을 새로운 컨트롤러/메서드로 구현할 수 있어 관리가 용이합니다. NestJS는 요청이 들어올 때 URI의 버전에 따라 해당 버전을 지원하는 컨트롤러와 핸들러를 찾아주므로, 클라이언트는 /v1/...
또는 /v2/...
와 같이 원하는 버전을 명시하여 호출하면 됩니다.
Swagger 설정 - 단일 문서 통합
이제 Swagger를 설정하여 버전이 적용된 API 엔드포인트들을 문서화해보겠습니다. 앞서 enableVersioning()
을 먼저 호출했기 때문에, 이제 Swagger 문서를 생성하면 각 경로에 자동으로 버전 prefix가 포함된 상태로 스펙이 만들어집니다. 한 가지 접근 방식은 **"단일 Swagger 문서"**를 생성하여 모든 버전의 API를 한꺼번에 보여주는 것입니다. 소규모 서비스나 API 변경 범위가 크지 않은 경우, 한 개의 Swagger UI에서 v1과 v2 엔드포인트를 모두 열람할 수 있어 편리합니다.
단일 문서 전략에서는 NestJS 애플리케이션 전체를 대상으로 한 번의 SwaggerModule.createDocument()
호출로 문서를 생성합니다. DocumentBuilder
를 이용해 기본적인 정보(title, description, version 등)를 설정하고 문서를 만든 뒤, SwaggerModule.setup()
으로 Swagger UI 엔드포인트를 등록합니다. 앞서 main.ts
예시에서는 setupSwagger(app)
함수를 별도로 분리했는데, 그 구현은 다음과 같습니다:
// setupSwagger.ts
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { INestApplication } from '@nestjs/common';
export function setupSwagger(app: INestApplication) {
// Swagger 설정 생성
const config = new DocumentBuilder()
.setTitle('Reservation API')
.setDescription('Reservation 서비스 API 문서')
.setVersion('1.0')
.build();
// 애플리케이션 전체를 대상으로 Swagger 문서 생성 (모든 버전 포함)
const document = SwaggerModule.createDocument(app, config);
// Swagger UI 엔드포인트 설정 (예: http://localhost:3000/docs)
SwaggerModule.setup('docs', app, document);
}
위 설정으로 애플리케이션의 모든 등록된 경로를 스캔하여 OpenAPI 문서(document
객체)를 생성합니다. 이 문서에는 현재 애플리케이션에 존재하는 v1, v2 모든 버전의 엔드포인트가 포함됩니다. 예를 들어 v1과 v2 두 가지 버전의 reservations
경로들이 각각 /v1/reservations
, /v2/reservations
로 문서화되어 한 Swagger UI에서 모두 확인할 수 있습니다. Swagger UI는 위 예시에서 /docs
경로로 제공되므로, 브라우저에서 http://<서버주소>/docs
로 접속하면 됩니다.
참고: 전역 경로(prefix)
app.setGlobalPrefix()
를 사용해/api/v1
형식으로 경로를 구성하는 경우, Swagger 설정 시 전역 프리픽스를 무시하거나 경로를 조정해야 합니다.SwaggerModule.createDocument
의 옵션으로ignoreGlobalPrefix: true
를 주면 전역 프리픽스를 문서에서 생략할 수 있고, 또는SwaggerModule.setup()
의 경로에:version
과 같은 동적 세그먼트를 사용해 Swagger UI를 각 버전에 맞게 제공할 수도 있습니다. 단일 문서 접근 방식에서는 일반적으로 한 개의 UI에서 모든 버전을 보여주지만, 필요에 따라 이러한 설정으로 Swagger UI 접근 경로를 튜닝할 수 있습니다.
Swagger 설정 - 다중 문서 분리
API 규모가 커지거나, 모노레포(monorepo) 구조로 여러 모듈이 공존하는 경우 혹은 마이크로서비스 경계를 나눠 별도의 API 문서를 관리해야 하는 경우에는 "다중 Swagger 문서" 전략이 유용합니다. 이 접근 방식에서는 API 버전별 또는 모듈별로 별도의 Swagger 스펙과 UI를 만들어 제공합니다. 예를 들어, v1 API는 /v1/docs
에서 문서화하고 v2 API는 /v2/docs
에서 별도 UI로 제공할 수 있습니다 (혹은 예약 서비스
와 결제 서비스
를 각각 다른 문서로 노출하는 식으로 모듈별 분리도 가능). 이렇게 하면 특정 버전이나 도메인에 해당하는 엔드포인트만 선별하여 문서화할 수 있어 문서가 방대해지는 것을 방지하고 관리 포인트를 분리할 수 있습니다.
NestJS Swagger 모듈은 이러한 다중 명세 지원을 기본 제공하는데, 이를 위해 애플리케이션을 모듈화하고 SwaggerModule.createDocument()
호출 시 include
옵션을 활용하면 됩니다. createDocument
의 세 번째 인자로 SwaggerDocumentOptions
객체를 전달하여, 그 안에 포함시킬 모듈 목록을 include
배열로 지정하면 해당 모듈들에 속한 경로만을 스캔하여 문서를 생성합니다. 공식 문서의 예제를 보면 CatsModule과 DogsModule을 개별 포함하여 두 개의 스펙을 만들고, 각각 별도의 Swagger UI 엔드포인트에 노출할 수 있다고 설명합니다.
다음은 버전별로 모듈을 분리하여 Swagger 문서를 생성하는 코드 예시입니다:
// 예시: v1, v2 모듈이 분리된 경우의 Swagger 문서 설정
import { ReservationsModuleV1 } from './reservations-v1.module';
import { ReservationsModuleV2 } from './reservations-v2.module';
// ... (NestFactory로 app 생성 등은 생략)
const v1Config = new DocumentBuilder()
.setTitle('Reservation API - v1')
.setDescription('Reservation API (버전 1)')
.setVersion('1.0')
.build();
// v1 모듈만 포함한 Swagger 문서 생성
const documentV1 = SwaggerModule.createDocument(app, v1Config, {
include: [ReservationsModuleV1],
});
SwaggerModule.setup('api/v1/docs', app, documentV1); // v1 문서 UI 엔드포인트
const v2Config = new DocumentBuilder()
.setTitle('Reservation API - v2')
.setDescription('Reservation API (버전 2)')
.setVersion('2.0')
.build();
// v2 모듈만 포함한 Swagger 문서 생성
const documentV2 = SwaggerModule.createDocument(app, v2Config, {
include: [ReservationsModuleV2],
});
SwaggerModule.setup('api/v2/docs', app, documentV2); // v2 문서 UI 엔드포인트
위 코드에서는 ReservationsModuleV1
과 ReservationsModuleV2
두 모듈을 가정하고, 각각의 모듈을 포함하는 별도의 Swagger 문서를 만들었습니다. include
옵션에 배열로 모듈을 넘기면 해당 모듈 및 그 하위에 연결된 모든 컨트롤러 경로들만 반영된 OpenAPI 스펙이 생성됩니다. 이렇게 생성된 documentV1
, documentV2
를 서로 다른 경로(/api/v1/docs
, /api/v2/docs
)에 SwaggerModule.setup()
으로 설정하여, 버전 1용 Swagger UI와 버전 2용 Swagger UI를 분리해 제공합니다. 이제 클라이언트나 개발자는 /api/v1/docs
에서 v1 API만, /api/v2/docs
에서 v2 API만 각각 확인할 수 있습니다.
팁: 이 접근 방식은 버전뿐 아니라 기능별 모듈로 문서를 분리하는 경우에도 활용할 수 있습니다. 예를 들어 _Public API_와 _Admin API_를 각각 다른 모듈로 관리하면서 Swagger 문서를 별도로 제공하거나, 마이크로서비스별로 문서를 따로 생성하여 서비스 경계마다 문서를 참조하게 할 수 있습니다.
include
옵션에 원하는 모듈 조합을 넘기면 필요한 엔드포인트 그룹만 문서화할 수 있습니다.
각 접근 방식의 활용 시나리오
앞서 살펴본 단일 문서 통합과 다중 문서 분리 두 가지 Swagger 통합 전략은 각각 장단점이 있으며, 사용할 상황이 다릅니다. 프로젝트의 규모와 요구 사항에 따라 알맞은 방식을 선택하면 됩니다:
-
단일 Swagger 문서: API 규모가 크지 않거나 모든 버전의 엔드포인트를 한 곳에서 관리하고자 할 때 적합합니다. 예를 들어 소규모 서비스에서는 v1과 v2를 굳이 분리하지 않고 한 번에 보여주는 편이 개발자에게 편리할 수 있습니다. 한 개의 Swagger UI에서 전체 API 스펙을 조회할 수 있어 검색과 참조가 쉽고, 설정도 비교적 단순합니다. 버전 간 변화가 비교적 적거나, 한 문서에서 버전별 경로만 구분하면 충분한 경우에 유용합니다. (NestJS의 URI 버저닝 적용만으로 Swagger에 각 경로가
/v1/
,/v2/
처럼 표시되므로 한눈에 버전을 구분할 수 있습니다.) -
다중 Swagger 문서: 대규모 모놀리식 또는 모노레포 프로젝트에서 모듈별로 API가 방대하게 존재하는 경우나, 마이크로서비스 경계를 명확히 나누어 문서화해야 할 경우 유용합니다. 또한 API 버전을 개별적으로 배포하거나 관리하는 시나리오에서도 각 버전별로 문서를 분리하면 혼선을 줄일 수 있습니다. 예를 들어 v2를 개발 중인 동안 v1 문서는 안정적으로 별도 제공하고, 준비되면 v2 문서를 공개하는 전략을 취할 수 있습니다. 이 방식은 각 문서가 경량화되어 로드 속도가 빨라지고 가독성이 향상되며, 권한 제어도 버전별로 분리하기가 수월합니다 (예: 내부 API 문서는 내부용으로만 노출하고 공개 API 문서는 따로 분리).
요약하면, 작은 규모나 단일 서비스라면 모든 버전을 하나의 Swagger 문서로 관리하는 편이 간편하며, 대규모 서비스나 다수의 모듈을 갖는 경우에는 버전/모듈별로 Swagger 문서를 분리하여 유지보수성과 명확성을 높이는 것이 좋습니다. 두 접근 방식 모두 NestJS에서 공식적으로 지원되므로, 필요에 따라 적절히 선택하여 활용하면 됩니다.
각 방법을 구현하는 코드는 위에서 살펴본 대로 비교적 간단하며, NestJS의 버전 관리 및 Swagger 모듈 기능을 조합하여 유연하게 API 문서를 구성할 수 있습니다. 올바르게 설정만 한다면, NestJS는 버전별로 호환성 유지와 문서화를 손쉽게 지원하므로, 안심하고 API를 발전시켜 나갈 수 있을 것입니다.
참고 자료: NestJS 공식 문서 및 GitHub 이슈 등을 통해 API 버저닝 및 Swagger 통합에 대한 추가 정보를 얻을 수 있습니다. (예: NestJS Docs - Versioning, OpenAPI/Swagger 섹션) 이번 가이드에서 다룬 내용은 현업에서도 많이 사용하는 패턴으로, NestJS 기반 프로젝트의 API 설계와 문서 전략 수립에 도움이 되길 바랍니다.